//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            Expression.cpp
// AUTHOR:          Vladimir Gumenuk
// DATE:            4-Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xort.h"
#include "xruntime.h"
#include "dllmain.h"
#include "DebugProperty.h"
#include "DebugStack.h"
#include "EventBase.h"
#include "Expression.h"

// CExpression
CExpression::CExpression()
{
    m_dwFlags         = 0;
    m_dwTimeout       = 0;
    m_iRadix          = 10;
    m_bAborted        = FALSE;
    m_bAsync          = FALSE;
    m_pFramePropStack = NULL;
    m_dwStackBase     = 0;
}

CExpression::~CExpression()
{

}

HRESULT CExpression::FinalConstruct()
{
    return S_OK;
}

void CExpression::FinalRelease()
{
}

STDMETHODIMP CExpression::Initialize( LPCOLESTR lpszText, IUnknown *pFramePropStack, DWORD dwStackBase, UINT nRadix )
{
    HRESULT hr = S_OK;

    m_iRadix          = nRadix;
    m_pFramePropStack = (CFramePropertyStack *)pFramePropStack;
    m_dwStackBase     = dwStackBase;

    DWORD dwAnsiLen = 2 * ( wcslen( lpszText ) ) + 1;  
    char *pszAnsiCode = (char *)malloc( dwAnsiLen );
    if( pszAnsiCode )
    {  
        WideCharToMultiByte( CP_ACP, 0, lpszText, -1, pszAnsiCode, dwAnsiLen, NULL, NULL ); 
        m_csText.Attach( pszAnsiCode );
        ATLTRACE( "CExpression::Initialize() -> '%s'\n", m_csText.GetBuffer() );

    } else
        hr = E_OUTOFMEMORY;

    return hr;
}

STDMETHODIMP CExpression::Evaluate( IDebugProperty2 **ppResult )
{
    Symbol *pTargetSymbol = NULL;

    if( m_pFramePropStack ) // first try to find symbol in local frame, despite where user pointed his mouse...
    {
        dword dwNumChildFrames = m_pFramePropStack->count();
        while( dwNumChildFrames-- )
        {
            LPPROPLIST pList = m_pFramePropStack->get( dwNumChildFrames ); // go from the top-most frame

            PROPERTIES_LIST::iterator theIter = pList->begin();
            IDebugProperty2 *pProp = theIter == NULL ? NULL : (IDebugProperty2*)theIter;

            while( pProp )
            {
                Symbol *pLocal = NULL;

                CComQIPtr<IXProperty> spXLocal( pProp );
                if( SUCCEEDED( spXLocal->GetSymbol( (IUnknown**)&pLocal ) ) )
                {
                    if( pLocal->m_csName == m_csText )
                    {
                        pTargetSymbol = pLocal;
                        break;
                    }
                }

                theIter++;
                pProp = theIter == NULL ? NULL : (IDebugProperty2*)theIter;
            }

            if( pTargetSymbol )
                break;
        }
    }

    if( !pTargetSymbol ) // if not found, try to find it in global symbol table...
    {
        CComPtr<IXProgram> spXProg = _AtlModule.getXProgramNoAddRef();

        SYMBOL_LIST *pSymTab;
        spXProg->GetGlobalSymbolTable( (IUnknown**)&pSymTab );

        SymbolRef sym = FindSymbol( *pSymTab, m_csText, FALSE );
        if( sym.isNULL() ) {
            return E_FAIL; // nothing found...
        } else {
            pTargetSymbol = &sym;
        }
    }

    CComPtr<IXProperty> spXproperty;
    HRESULT hr = CDebugProperty::Create( &spXproperty );
    if( SUCCEEDED( hr ) )
    {
        DWORD dwFlags = ( pTargetSymbol->m_dwStorage == cSTORAGE_STACK ) ? DBG_PROP_LOCALVAR : DBG_PROP_GLOBALSYMBOL;

        if( SUCCEEDED( hr = spXproperty->Initialize( (IUnknown*)pTargetSymbol, m_dwStackBase, NULL, NULL, dwFlags ) ) )
        {
            CComQIPtr<IDebugProperty2> spProp( spXproperty );

            if( m_bAsync ) // will be send from main thread
            {
                CExpressionEvaluationCompleteEvent *pEvent = new CExpressionEvaluationCompleteEvent( this, spProp );
                pEvent->Send();
            }
            else // just return the property to caller
            {
                *ppResult = spProp.Detach();
            }
        }
    }

    return hr;
}

STDMETHODIMP CExpression::EvaluateAsync( EVALFLAGS dwFlags, IDebugEventCallback2 *pExprCallback )
{
    m_bAsync     = TRUE;
    m_dwFlags    = dwFlags;
    m_spCallback = pExprCallback;

    AddRef();
    _AtlModule.PostMsgToMainThread( WM_EXPR_EVAL, (WPARAM)this );

    return S_OK;
}

// When asynchronous expression evaluation is canceled, do not sent an IDebugExpressionEvaluationCompleteEvent2 event 
// to the event callback passed to the IDebugProgram2::Attach or IDebugEngine2::Attach methods.
STDMETHODIMP CExpression::Abort()
{
    m_bAborted = TRUE;
    return S_OK;
}

STDMETHODIMP CExpression::EvaluateSync( EVALFLAGS dwFlags, DWORD dwTimeout, IDebugEventCallback2 *pExprCallback, IDebugProperty2 **ppResult )
{
    if( !ppResult )
        return E_INVALIDARG;

    m_bAsync    = FALSE;
    m_dwTimeout = dwTimeout;
    m_dwFlags   = dwFlags;

    return Evaluate( ppResult );
}
